在 Android 的開發中,難免避免不掉使用多線程,其中 Handler 是多線程中,不可或缺的角色。
但這邊不是示範如何使用 Handler,而是示範透過 Robolectric 來測試 Handler。
Environment:
Robolectric Ver. 2.4
Mac OS X 10.10
Android Studio 1.0.2
首先,先準備好 SetUp 和 TearDown 方法。
接著,準備兩個受測程式:
runnable 每執行一次就 counter 加1
handler 每 handlerMessage 一次就 counter 加10
HandlerTest.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 import android.os.Handler;import android.os.Message;import junit.framework.TestCase;import org.junit.After;import org.junit.Before;import org.junit.Test;import org.junit.runner.RunWith;import org.robolectric.RobolectricTestRunner;import org.robolectric.annotation.Config;@RunWith (RobolectricTestRunner.class)@Config (emulateSdk = 18 )public class HandlerTest extends TestCase { int counter; Runnable runnable; Handler handler; @Before public void setUp () throws Exception { super .setUp(); counter = 0 ; runnable = new Runnable() { @Override public void run () { counter++; } }; handler = new Handler() { @Override public void handleMessage (Message msg) { super .handleMessage(msg); counter += 10 ; } }; } @After public void tearDown () throws Exception { runnable = null ; handler = null ; super .tearDown(); } }
然後我們逐一加上 TestCase
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 @Test public void testM1 () throws Exception { assertEquals(0 ,counter); handler.sendEmptyMessage(0 ); assertEquals(10 , counter); handler.sendEmptyMessageDelayed(0 , 0 ); assertEquals(20 ,counter); handler.sendEmptyMessageDelayed(0 ,1 ); handler.sendEmptyMessageDelayed(0 ,1000 ); handler.sendEmptyMessageDelayed(0 ,10000 ); assertEquals(20 , counter); } @Test public void testR1 () throws Exception { assertEquals(0 ,counter); handler.post(runnable); assertEquals(1 , counter); handler.postDelayed(runnable,0 ); assertEquals(2 ,counter); handler.postDelayed(runnable,1 ); handler.postDelayed(runnable,1000 ); handler.postDelayed(runnable,10000 ); assertEquals(2 , counter); }
從這個測試來看,我們可以明顯看到最後三條 handler.sendEmptyMessageDelayed
並沒有執行的效果。
這個時候我們可能會覺得說,應該是執行的時間不夠,所以這個測試案例並不能正常跑完。
所以,在這最後面加入 Thread.sleep(10000)
,期望他能夠順利跑完
PS : Don’t do that, it only let your test wait 10 sec, but handlerMessage still not working handler.sendEmptyMessage(0)
和 handler.sendEmptyMessageDelayed(0, 0)
屬於立即執行,所以馬上可以得到結果
不該使用 Thread.sleep(10000)
, Robolectric 有提供一些好用的方法(idle
)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 @Test public void testM2 () throws Exception { handler.sendEmptyMessageDelayed(0 ,1 ); handler.sendEmptyMessageDelayed(0 ,1000 ); handler.sendEmptyMessageDelayed(0 ,10000 ); Robolectric.idleMainLooper(10000 ); assertEquals(30 , counter); handler.sendEmptyMessageDelayed(0 ,1 ); handler.sendEmptyMessageDelayed(0 ,1000 ); handler.sendEmptyMessageDelayed(0 ,10000 ); ShadowLooper.idleMainLooper(10000 ); assertEquals(60 , counter); ShadowLooper shadowLooper = Robolectric.shadowOf(handler.getLooper()); handler.sendEmptyMessageDelayed(0 ,1 ); handler.sendEmptyMessageDelayed(0 ,1000 ); handler.sendEmptyMessageDelayed(0 ,10000 ); shadowLooper.idle(10000 ); assertEquals(90 , counter); } @Test public void testR2 () throws Exception { handler.postDelayed(runnable,1 ); handler.postDelayed(runnable,1000 ); handler.postDelayed(runnable,10000 ); Robolectric.idleMainLooper(10000 ); assertEquals(3 , counter); handler.postDelayed(runnable,1 ); handler.postDelayed(runnable,1000 ); handler.postDelayed(runnable,10000 ); ShadowLooper.idleMainLooper(10000 ); assertEquals(6 , counter); ShadowLooper shadowLooper = Robolectric.shadowOf(handler.getLooper()); handler.postDelayed(runnable,1 ); handler.postDelayed(runnable,1000 ); handler.postDelayed(runnable,10000 ); shadowLooper.idle(10000 ); assertEquals(9 , counter); }
以及我們可透過操控 Looper 的方式,去掌控 handler。
用 shadowLooper.runOneTask 一次執行一個 Task。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 @Test public void testM3() throws Exception{ ShadowLooper shadowLooper = Robolectric.shadowOf(handler.getLooper()); handler.sendEmptyMessageDelayed(0,1); shadowLooper.runOneTask(); assertEquals(10, counter); handler.sendEmptyMessageDelayed(0,1000); shadowLooper.runOneTask(); assertEquals(20, counter); handler.sendEmptyMessageDelayed(0,10000); shadowLooper.runOneTask(); assertEquals(30, counter); } @Test public void testR3() throws Exception{ ShadowLooper shadowLooper = Robolectric.shadowOf(handler.getLooper()); handler.postDelayed(runnable,1); shadowLooper.runOneTask(); assertEquals(1, counter); handler.postDelayed(runnable,1000); shadowLooper.runOneTask(); assertEquals(2, counter); handler.postDelayed(runnable,10000); shadowLooper.runOneTask(); assertEquals(3, counter); }
或者用 shadowLooper.runToEndOfTasks
一次把所有的 Tasks 執行完。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Test public void testM4 () throws Exception { ShadowLooper shadowLooper = Robolectric.shadowOf(handler.getLooper()); handler.postDelayed(runnable,1 ); handler.postDelayed(runnable,1000 ); handler.postDelayed(runnable,10000 ); shadowLooper.runToEndOfTasks(); assertEquals(3 , counter); } @Test public void testR4 () throws Exception { ShadowLooper shadowLooper = Robolectric.shadowOf(handler.getLooper()); handler.sendEmptyMessageDelayed(0 ,1 ); handler.sendEmptyMessageDelayed(0 ,1000 ); handler.sendEmptyMessageDelayed(0 ,10000 ); shadowLooper.runToEndOfTasks(); assertEquals(30 , counter); }
除了 runOneTask
之外,還有一個相似的方法 runToNextTask
上半段送出了三個同時間的 Task,並要求執行一個任務,因此結果為”10”
下半段也送出了三個同時間的 Task,並要求把最近一次的 tasks(b1, b2, b3)做執行,因此結果為”10 + 30”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Test public void test5 () throws Exception { ShadowLooper shadowLooper = Robolectric.shadowOf(handler.getLooper()); handler.sendEmptyMessageDelayed(0 ,1 ); handler.sendEmptyMessageDelayed(0 ,1 ); handler.sendEmptyMessageDelayed(0 ,1 ); shadowLooper.runOneTask(); handler.removeCallbacksAndMessages(null ); shadowLooper.runToEndOfTasks(); assertEquals(10 ,counter); handler.sendEmptyMessageDelayed(0 ,1 ); handler.sendEmptyMessageDelayed(0 ,1 ); handler.sendEmptyMessageDelayed(0 ,1 ); shadowLooper.runToNextTask(); handler.removeCallbacksAndMessages(null ); shadowLooper.runToEndOfTasks(); assertEquals(40 ,counter); }
下面的案例,邊寫才發現一些蹊蹺,稍微翻了下Source,shadowLooper會把丟進來的task交給scheduler處理, 依照current time and delay time 進行sort,並放在MessageQueue中,因為這樣的設計,我們丟進去的任務才不會亂掉
當我們執行runOneTask
或者 runToNextTask
,就會從MessageQueue中拿一個任務出來,並做了時間推移。
接下來用test6
來解釋!!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Test public void test6 () throws Exception { ShadowLooper shadowLooper = Robolectric.shadowOf(handler.getLooper()); handler.sendEmptyMessageDelayed(0 ,1 ); shadowLooper.runOneTask(); assertEquals(1 ,shadowLooper.getScheduler().getCurrentTime()); handler.sendEmptyMessageDelayed(0 ,1 ); shadowLooper.runOneTask(); assertEquals(2 ,shadowLooper.getScheduler().getCurrentTime()); handler.sendEmptyMessageDelayed(0 ,1 ); shadowLooper.runOneTask(); assertEquals(3 ,shadowLooper.getScheduler().getCurrentTime()); handler.sendEmptyMessageDelayed(0 ,1 ); handler.sendEmptyMessageDelayed(0 ,1 ); handler.sendEmptyMessageDelayed(0 ,1 ); shadowLooper.runOneTask(); shadowLooper.runOneTask(); shadowLooper.runOneTask(); assertEquals(4 ,shadowLooper.getScheduler().getCurrentTime()); }
另外一個坑是removeCallbacksAndMessages(null)
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 @Test public void test7 () throws Exception { ShadowLooper shadowLooper = Robolectric.shadowOf(handler.getLooper()); handler.sendEmptyMessageDelayed(0 ,1 ); handler.sendEmptyMessageDelayed(0 ,1000 ); handler.sendEmptyMessageDelayed(0 ,10000 ); handler.sendEmptyMessageDelayed(0 ,20000 ); shadowLooper.runOneTask(); handler.removeCallbacksAndMessages(null ); assertEquals(10 , counter); handler.sendEmptyMessageDelayed(0 ,1 ); handler.sendEmptyMessageDelayed(0 ,1000 ); handler.sendEmptyMessageDelayed(0 ,10000 ); handler.sendEmptyMessageDelayed(0 ,20000 ); shadowLooper.runOneTask(); assertEquals(20 , counter); assertEquals(2 ,shadowLooper.getScheduler().getCurrentTime()); shadowLooper.runOneTask(); assertEquals(20 , counter); assertEquals(1000 ,shadowLooper.getScheduler().getCurrentTime()); shadowLooper.runOneTask(); assertEquals(30 , counter); assertEquals(1001 ,shadowLooper.getScheduler().getCurrentTime()); shadowLooper.runOneTask(); assertEquals(30 , counter); assertEquals(10000 ,shadowLooper.getScheduler().getCurrentTime()); shadowLooper.runOneTask(); assertEquals(40 , counter); assertEquals(10001 ,shadowLooper.getScheduler().getCurrentTime()); shadowLooper.runOneTask(); assertEquals(40 , counter); assertEquals(20000 ,shadowLooper.getScheduler().getCurrentTime()); shadowLooper.runOneTask(); assertEquals(50 , counter); assertEquals(20001 ,shadowLooper.getScheduler().getCurrentTime()); }
接著是idleConstantly
,預設為false,打開之後相當於自排效果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Test public void test8 () throws Exception { ShadowLooper shadowLooper = Robolectric.shadowOf(handler.getLooper()); shadowLooper.idleConstantly(true ); handler.sendEmptyMessageDelayed(0 ,1 ); assertEquals(10 , counter); handler.sendEmptyMessageDelayed(0 ,1000 ); assertEquals(20 , counter); handler.sendEmptyMessageDelayed(0 ,10000 ); assertEquals(30 , counter); handler.sendEmptyMessageDelayed(0 ,20000 ); assertEquals(40 , counter); assertEquals(0 ,shadowLooper.getScheduler().getCurrentTime()); }
Source Code At Gists
End